package com.chen.blue.service.business.impl;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.chen.blue.annotation.permission.DataPermission;
import com.chen.blue.base.FileBase;
import com.chen.blue.container.PermissionFlag;
import com.chen.blue.converter.business.ContestApplyActiveConverter;
import com.chen.blue.converter.business.ContestPersonInfoConverter;
import com.chen.blue.converter.business.ContestResultConverter;
import com.chen.blue.converter.business.ResourceConverter;
import com.chen.blue.dictionary.AuditDict;
import com.chen.blue.dictionary.CommonDict;
import com.chen.blue.dictionary.ResourceType;
import com.chen.blue.dictionary.VersionDict;
import com.chen.blue.domain.delete.business.ContestApplyActiveDelete;
import com.chen.blue.domain.dto.business.ContestApplyActiveDto;
import com.chen.blue.domain.dto.business.ContestResultDto;
import com.chen.blue.domain.dto.business.update.ContestApplyActiveDtoEdit;
import com.chen.blue.domain.model.business.*;
import com.chen.blue.domain.query.business.ContestApplyActiveQuery;
import com.chen.blue.domain.vo.business.CompetitionItemVo;
import com.chen.blue.domain.vo.business.CompetitionVo;
import com.chen.blue.domain.vo.business.ContestApplyActiveVo;
import com.chen.blue.exception.e.DtoNullException;
import com.chen.blue.mapper.business.ContestApplyActiveMapper;
import com.chen.blue.oa.AbstractOaRequestHandler;
import com.chen.blue.oa.OaRequest;
import com.chen.blue.oa.handler.*;
import com.chen.blue.result.DataResult;
import com.chen.blue.service.business.*;
import com.chen.blue.service.candidate.CandidateInfoService;
import com.chen.blue.service.manager.AsyncManager;
import com.chen.blue.service.manager.factory.AsyncFactory;
import com.chen.blue.service.security.SecurityService;
import com.chen.blue.utils.MinioUtil;
import com.chen.blue.utils.PageInfoUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 竞赛申请活动表 服务实现类
 * </p>
 *
 * @author CLT
 * @since 2024-05-23
 */
@Service("iContestApplyActiveService")
public class IContestApplyActiveServiceImpl extends ServiceImpl<ContestApplyActiveMapper, ContestApplyActiveModel> implements IContestApplyActiveService {

    @Resource
    private IResourceService iResourceService;

    @Resource
    private IContestApplyRecordService iContestApplyRecordService;

    @Resource
    private IContestPersonInfoService iContestPersonInfoService;

    @Resource
    private IContestApplyService iContestApplyService;

    @Resource
    private ICompetitionService iCompetitionService;

    @Resource
    private ICompetitionItemService iCompetitionItemService;

    @Resource
    private IContestResultService iContestResultService;

    /**
     * 竞赛申报
     * <p>
     * <p>
     * 该方法支持：保存，上报
     * 保存：只添加活动表及其资源
     * 上报：添加活动表及其资源的同时也要生成对应的记录
     *
     * @param contestApplyActiveDto
     * @return
     * @throws DtoNullException
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean addContestApplyActive(ContestApplyActiveDto contestApplyActiveDto) throws DtoNullException, IOException {

        if (Objects.isNull(contestApplyActiveDto))
            throw new DtoNullException("参数为空");

        // 保存功能暂时不支持
        contestApplyActiveDto.setOptionType(CommonDict.OPTION_TYPE_REPORT);

        if (!(CommonDict.OPTION_TYPE_REPORT.equals(contestApplyActiveDto.getOptionType())) && !(CommonDict.OPTION_TYPE_SAVE.equals(contestApplyActiveDto.getOptionType())))
            throw new DtoNullException("操作类型不规范");

        // 验证此次竞赛申请是否已经被别人申请，如果已经被申请，则不能进行操作
        // validContestEnabled(contestApplyActiveDto);

        // 如果当前赛事及赛项已经存在，则跳过流程一，直接进入流程二
        if (validContestIsHas(contestApplyActiveDto)) return competitionHasExistHandler(contestApplyActiveDto);

        CandidateInfoService.setCreator(contestApplyActiveDto);

        ContestApplyActiveModel contestApplyActiveModel = ContestApplyActiveConverter.dtoToModel(contestApplyActiveDto);

        ContestApplyRecordModel contestApplyRecordModel = null;

        contestApplyActiveModel.setDeptId(SecurityService.currentUser().getSysUser().getDeptId());

        // 设置状态
        if (CommonDict.OPTION_TYPE_REPORT.equals(contestApplyActiveDto.getOptionType())) {
            // 上报
            setStatus(AuditDict.STAGE_STAGE_FIRST, AuditDict.ROLE_INNER_FACULTY_MANAGER_CODE_ID, AuditDict.ROLE_INNER_NO, AuditDict.CURRENT_STATUS_NOT_AUDIT, contestApplyActiveModel);
            contestApplyRecordModel = new ContestApplyRecordModel();
            BeanUtils.copyProperties(contestApplyActiveModel, contestApplyRecordModel);

            CandidateInfoService.setCreator(contestApplyRecordModel);
        } else if (CommonDict.OPTION_TYPE_SAVE.equals(contestApplyActiveDto.getOptionType())) {
            // 保存
            setStatus(AuditDict.STAGE_STAGE_FIRST, AuditDict.ROLE_INNER_NO, AuditDict.AUDIT_STATUS_NO, AuditDict.CURRENT_STATUS_NOT_REPORT, contestApplyActiveModel);
        }

        boolean b1 = (CommonDict.OPTION_TYPE_REPORT.equals(contestApplyActiveDto.getOptionType()) || CommonDict.OPTION_TYPE_SAVE.equals(contestApplyActiveDto.getOptionType())) && super.save(contestApplyActiveModel);

        // 上报操作
        if (CommonDict.OPTION_TYPE_REPORT.equals(contestApplyActiveDto.getOptionType()) && Objects.nonNull(contestApplyRecordModel)) {

            // 设置活动表与记录表的关联（用于以后修改版本更新）并生成记录
            contestApplyRecordModel.setRelation(contestApplyActiveModel.getId());
            contestApplyRecordModel.setVersion(String.valueOf(VersionDict.VERSION_1));
            iContestApplyRecordService.save(contestApplyRecordModel);

        }

        // 异步上传文件，防止文件过大，接口响应慢
        fileUploadAsync(contestApplyActiveModel, contestApplyRecordModel, contestApplyActiveDto);

        return b1;

    }

    /**
     * 对于流程一填写的赛事及赛项是赛事管理里面已经存在的，则不需要走流程一，直接到流程二
     *
     * @return
     */
    protected boolean competitionHasExistHandler(ContestApplyActiveDto contestApplyActiveDto) {

        ContestApplyActiveModel contestApplyActiveModel = ContestApplyActiveConverter.dtoToModel(contestApplyActiveDto);

        // 自动填充赛事数据
        ContestApplyModel contestApplyModel = new ContestApplyModel();
        LambdaQueryWrapper<CompetitionModel> competitionWrapper = new LambdaQueryWrapper<>();
        competitionWrapper.eq(CompetitionModel::getCompetitionName, contestApplyActiveDto.getEventName());
        Long competitionId = iCompetitionService.getOne(competitionWrapper).getId();
        LambdaQueryWrapper<CompetitionItemModel> itemWrapper = new LambdaQueryWrapper<>();
        itemWrapper.eq(CompetitionItemModel::getCompetitionId, competitionId);
        itemWrapper.eq(CompetitionItemModel::getItemName, contestApplyActiveDto.getCompetitionName());
        CompetitionItemModel competitionItemModel = iCompetitionItemService.getOne(itemWrapper);

        // 将流程状态设置为流程二待上报状态并生成相关的流程
        BeanUtils.copyProperties(competitionItemModel, contestApplyActiveModel, ignoreAttribute(Arrays.asList("creator")));
        CandidateInfoService.setCreator(contestApplyActiveModel);
        setStatus(AuditDict.STAGE_STAGE_SECOND, AuditDict.ROLE_INNER_NO, AuditDict.ROLE_INNER_NO, AuditDict.CURRENT_STATUS_NOT_REPORT, contestApplyActiveModel);
        boolean b1 = super.save(contestApplyActiveModel);

        // 生成竞赛表，后续填报人员信息
        BeanUtils.copyProperties(competitionItemModel, contestApplyModel, ignoreAttribute(Arrays.asList("creator")));
        contestApplyModel.setEventName(contestApplyActiveDto.getEventName());
        contestApplyModel.setCompetitionName(contestApplyActiveDto.getCompetitionName());
        contestApplyModel.setRelation(contestApplyActiveModel.getId());
        contestApplyModel.setCreator(SecurityService.currentUserName());
        boolean b2 = iContestApplyService.save(contestApplyModel);

        return b1 && b2;

    }


    /**
     * 用于竞赛的保存后再申报（todo）
     *
     * @return
     */
    @Override
    public boolean reportAfterSaveOrUpdate(ContestApplyActiveDto contestApplyActiveDto) throws DtoNullException {

        if (Objects.isNull(contestApplyActiveDto))
            throw new DtoNullException("参数为空");

        ContestApplyActiveModel contestApplyActiveModel = ContestApplyActiveConverter.dtoToModel(contestApplyActiveDto);

        ContestApplyRecordModel contestApplyRecordModel = null;


        // 上报
        setStatus(AuditDict.STAGE_STAGE_FIRST, AuditDict.ROLE_INNER_FACULTY_MANAGER_CODE_ID, AuditDict.ROLE_INNER_NO, AuditDict.CURRENT_STATUS_NOT_AUDIT, contestApplyActiveModel);
        contestApplyRecordModel = new ContestApplyRecordModel();
        BeanUtils.copyProperties(contestApplyActiveModel, contestApplyRecordModel);

        CandidateInfoService.setCreator(contestApplyRecordModel);

        boolean b1 = super.updateById(contestApplyActiveModel);
        // todo


        return false;
    }


    /**
     * 用于竞赛申报驳回修改和上报
     *
     * @param contestApplyActiveDto
     * @return
     * @throws DtoNullException
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean updateContestApplyActive(ContestApplyActiveDto contestApplyActiveDto) throws DtoNullException, IOException {

        if (Objects.isNull(contestApplyActiveDto) || Objects.isNull(contestApplyActiveDto.getId()))
            throw new DtoNullException("参数为空");

        if (!(CommonDict.OPTION_TYPE_REPORT.equals(contestApplyActiveDto.getOptionType())) && !(CommonDict.OPTION_TYPE_SAVE.equals(contestApplyActiveDto.getOptionType())))
            throw new DtoNullException("操作类型不规范");

        // 验证是否可以修改
        validUpdateEnabled(contestApplyActiveDto);

        CandidateInfoService.setUpdater(contestApplyActiveDto);

        ContestApplyActiveModel contestApplyActiveModel = ContestApplyActiveConverter.dtoToModel(contestApplyActiveDto);

        ContestApplyRecordModel contestApplyRecordModel = null;

        // 设置状态
        if (CommonDict.OPTION_TYPE_REPORT.equals(contestApplyActiveDto.getOptionType())) {
            // 上报
            setStatus(AuditDict.STAGE_STAGE_FIRST, AuditDict.ROLE_INNER_FACULTY_MANAGER_CODE_ID, AuditDict.AUDIT_STATUS_NO, AuditDict.CURRENT_STATUS_NOT_AUDIT, contestApplyActiveModel);
            contestApplyRecordModel = new ContestApplyRecordModel();
            BeanUtils.copyProperties(contestApplyActiveModel, contestApplyRecordModel);

            CandidateInfoService.setCreator(contestApplyRecordModel);
        } else if (CommonDict.OPTION_TYPE_SAVE.equals(contestApplyActiveDto.getOptionType())) {
            // 保存
            setStatus(AuditDict.STAGE_STAGE_FIRST, AuditDict.ROLE_INNER_NO, AuditDict.AUDIT_STATUS_NO, AuditDict.CURRENT_STATUS_NOT_REPORT, contestApplyActiveModel);
        }

        boolean b1 = (CommonDict.OPTION_TYPE_REPORT.equals(contestApplyActiveDto.getOptionType()) || CommonDict.OPTION_TYPE_SAVE.equals(contestApplyActiveDto.getOptionType())) && super.updateById(contestApplyActiveModel);

        // 上报操作
        if (CommonDict.OPTION_TYPE_REPORT.equals(contestApplyActiveDto.getOptionType()) && Objects.nonNull(contestApplyRecordModel)) {

            // 设置活动表与记录表的关联（用于以后修改版本更新）并生成记录
            contestApplyRecordModel.setRelation(contestApplyActiveModel.getId());
            ContestApplyRecordModel lastVersion = getLastVersion(contestApplyActiveModel.getId());
            if (Objects.isNull(lastVersion)) throw new DtoNullException("当前竞赛不存在");
            contestApplyRecordModel.setVersion(lastVersion == null ? String.valueOf(VersionDict.VERSION_1) : String.valueOf(getVersionPrefix(lastVersion.getVersion()) + VersionDict.VERSION_BIG_STEP));
            iContestApplyRecordService.save(contestApplyRecordModel);

        }

        // 将关联的文件资源删除，重新添加
        LambdaUpdateWrapper<ResourceModel> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(ResourceModel::getActiveId, contestApplyActiveModel.getId());
        wrapper.eq(ResourceModel::getType, ResourceType.RESOURCE_TYPE_FIRST);
        iResourceService.remove(wrapper);

        // 异步上传文件，防止文件过大，接口响应慢
        fileUploadAsync(contestApplyActiveModel, contestApplyRecordModel, contestApplyActiveDto);

        return b1;

    }

    /**
     * 该方法支持：上报；驳回修改
     * 上报：和流程二类似
     * 步骤：
     * 1. 验证当前流程状态是否可以进行该上报（流程默认在进行中）
     * 2. 开始记录流程
     * 3. 添加相关资源（如果是修改，则删除后添加）
     * 4. 将流程设置为待审核
     *
     * @param activeId
     * @param contestResultDto
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean addContestResult(Long activeId, ContestResultDto contestResultDto) throws DtoNullException {

        if (Objects.isNull(activeId))
            throw new DtoNullException("参数为空");

        LambdaQueryWrapper<ContestApplyModel> wrapperVar1 = new LambdaQueryWrapper<>();
        wrapperVar1.eq(ContestApplyModel::getRelation, activeId);
        Long applyId = iContestApplyService.getOne(wrapperVar1).getId();
        // 验证是否可以执行流程
        validContestResultEnabled(applyId);

        ContestApplyActiveModel applyActiveModel = super.getById(activeId);
        setStatus(AuditDict.STAGE_STAGE_THIRD, AuditDict.ROLE_INNER_FACULTY_MANAGER_CODE_ID, AuditDict.AUDIT_STATUS_NO, AuditDict.CURRENT_STATUS_NOT_AUDIT, applyActiveModel);

        // 开始第三阶段流程记录
        ContestApplyRecordModel recordModel = new ContestApplyRecordModel();
        BeanUtils.copyProperties(applyActiveModel, recordModel, ignoreAttribute(Collections.EMPTY_LIST));
        recordModel.setRelation(activeId);
        ContestApplyRecordModel lastVersion = getLastVersion(activeId);
        recordModel.setVersion(String.valueOf(getVersionPrefix(lastVersion.getVersion()) + VersionDict.VERSION_BIG_STEP));
        boolean b1 = iContestApplyRecordService.save(recordModel);

        // 如果有关联的竞赛结果信息，则删除，重新添加
        /*
        LambdaUpdateWrapper<ResourceModel> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(ResourceModel::getActiveId, activeId);
        wrapper.eq(ResourceModel::getType, ResourceType.RESOURCE_TYPE_SECOND);
        iResourceService.remove(wrapper);
        */
        LambdaUpdateWrapper<ContestResultModel> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(ContestResultModel::getActiveId, activeId);
        iContestResultService.remove(wrapper);

        // 记录表关联竞赛结果信息
        ContestResultModel contestResultModel = new ContestResultModel();
        BeanUtils.copyProperties(contestResultDto, contestResultModel);
        contestResultModel.setActiveId(activeId);
        contestResultModel.setRecordId(recordModel.getId());
        contestResultModel.setApplyId(applyId);
        boolean b2 = iContestResultService.save(contestResultModel);
        /*
        List<ResourceModel> resourceModels = new ArrayList<>();
        ResourceModel resourceModel = null;
        for (FileBase fileBase : fileBases) {
            resourceModel = new ResourceModel();
            resourceModel.setType(ResourceType.RESOURCE_TYPE_SECOND);
            resourceModel.setActiveId(activeId);
            resourceModel.setRecordId(recordModel.getId());
            resourceModel.setApplyId(applyId);
            BeanUtils.copyProperties(fileBase, resourceModel, ignoreAttribute());
            resourceModels.add(resourceModel);
        }
        boolean b2 = iResourceService.saveBatch(resourceModels);
        */

        // 修改流程状态
        boolean b3 = super.updateById(applyActiveModel);

        return b1 && b2 && b3;

    }

    /**
     * 该方法支持：上报；驳回修改
     * 上报：将流程激活，生成相关流程记录
     *
     * @param activeId
     * @return
     * @throws DtoNullException
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean addContestApplyRelationPerson(Long activeId) throws DtoNullException {

        if (Objects.isNull(activeId))
            throw new DtoNullException("参数为空");

        // 验证是否可以进行第二阶段流程
        validContestPersonInfoEnabled(activeId);

        ContestApplyActiveModel applyActiveModel = super.getById(activeId);
        if (Objects.isNull(applyActiveModel)) throw new DtoNullException("流程不存在");
        setStatus(AuditDict.STAGE_STAGE_SECOND, AuditDict.ROLE_INNER_FACULTY_MANAGER_CODE_ID, AuditDict.AUDIT_STATUS_NO, AuditDict.CURRENT_STATUS_NOT_AUDIT, applyActiveModel);
        // 开始第二阶段流程记录
        ContestApplyRecordModel recordModel = new ContestApplyRecordModel();
        BeanUtils.copyProperties(applyActiveModel, recordModel, ignoreAttribute(Collections.EMPTY_LIST));
        recordModel.setRelation(activeId);
        ContestApplyRecordModel lastVersion = getLastVersion(activeId);
        recordModel.setVersion(Objects.nonNull(lastVersion) ? String.valueOf(getVersionPrefix(lastVersion.getVersion()) + VersionDict.VERSION_BIG_STEP) : String.valueOf(VersionDict.VERSION_2));
        boolean b1 = iContestApplyRecordService.save(recordModel);
        // 记录表关联人员信息（如果没有填写抛出异常）
        validPersonInfoIsEnabled(activeId);
        LambdaUpdateWrapper<ContestPersonInfoModel> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(ContestPersonInfoModel::getActiveId, activeId);
        wrapper.set(ContestPersonInfoModel::getRecordId, recordModel.getId());
        wrapper.set(ContestPersonInfoModel::getUpdater, SecurityService.currentUserName());
        boolean b2 = iContestPersonInfoService.update(wrapper);
        // 修改流程状态
        boolean b3 = super.updateById(applyActiveModel);

        return b1 && b2 && b3;

    }

    /**
     * 我的审核（竞赛结果）
     * <p>
     * <p>
     * 根据当前登录的角色进行状态改变：<br/>
     * 初始值：3102 <br/>
     * 2.院部经办人(ROLE_faculty_manager)   stage_current->3  person_audit->1  audit_status->1,2  stage_current_status->2,4<br/>
     * 通过：将当前审核人的审核状态添加记录（3122），再将状态设置为下一个审核人（3202）<br/>
     * 驳回：将当前审核人的审核状态添加记录（3114），该版本流程结束，is_active设为0<br/>
     * 3.院部负责人(ROLE_faculty_charge)   stage_current->3  person_audit->2  audit_status->1,2  stage_current_status->2,4<br/>
     * （3222），（3302）<br/>
     * （3214）<br/>
     * 4.教务处经办人(ROLE_office_manager) stage_current->3  person_audit->3  audit_status->1,2  stage_current_status->2,4<br/>
     * （3322），（3402）<br/>
     * （3314）<br/>
     * 5.教务处负责人(ROLE_office_charge) stage_current->3  person_audit->4  audit_status->1,2  stage_current_status->2,4<br/>
     * （3422），（3502）<br/>
     * （3414）<br/>
     * 6.主管校长(ROLE_school_boss)    stage_current->3  person_audit->5  audit_status->1,2  stage_current_status->3,4<br/>
     * （3523），（0000）<br/>
     * （3514）<br/>
     * <p>
     * 状态说明：所有的状态管理在Oastatus和AuditStatus中
     * person_audit：1（院部经办人）2（院部负责人）3（教务处经办人）4（教务处负责人）5（主管校长） 0（无人审核）<br/>
     * audit_status：1（驳回）2（通过）0（待审核）<br/>
     * stage_current：1（竞赛申报阶段）2（备赛申报阶段）3（竞赛总结阶段）<br/>
     * stage_current_status：1（待上报）2（待审核）3（通过）4（驳回）<br/>
     * </p>
     * <p>
     * 步骤：
     * 1. 验证当前流程是否存在
     * 2. 验证当前流程是否在进行中
     * 3. 开始审核
     * 4. 验证是否夸段或者跨级审核
     * 5. 审核完成
     *
     * @param contestApplyActiveDtoEdit
     * @return
     * @throws DtoNullException
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean updateContestResult(ContestApplyActiveDtoEdit contestApplyActiveDtoEdit) throws DtoNullException {

        if (Objects.isNull(contestApplyActiveDtoEdit)) {
            throw new DtoNullException("参数为空");
        }

        Integer auditStatus = contestApplyActiveDtoEdit.getAuditStatus();
        Long id = contestApplyActiveDtoEdit.getId();
        // ROLE_..（当前审核人的所有的角色）
        List<String> roleCodeList = SecurityService.currentUserRoles();
        // 当前流程是否存在
        validIsEnabled(id);
        // 校验(流程是否在进行中)
        validAuditEnabled(id);

        // 设置状态
        OaRequest oaRequest = new OaRequest(roleCodeList, id, auditStatus);
        AbstractOaRequestHandler facultyChargeHandler = new FacultyChargeHandler()
                .setHandler(new FacultyManagerHandler()
                        .setHandler(new OfficeChargeHandler()
                                .setHandler(new OfficeManagerHandler()
                                        .setHandler(new SchoolBossHandler()))));

        return facultyChargeHandler.handlerRequestContestResult(oaRequest);

    }

    /**
     * 我的审核（人员信息）
     * <p>
     * <p>
     * 根据当前登录的角色进行状态改变：<br/>
     * 初始值：2102 <br/>
     * 2.院部经办人(ROLE_faculty_manager)   stage_current->2  person_audit->1  audit_status->1,2  stage_current_status->2,4<br/>
     * 通过：将当前审核人的审核状态添加记录（2122），再将状态设置为下一个审核人（2202）<br/>
     * 驳回：将当前审核人的审核状态添加记录（2114），该版本流程结束，is_active设为0<br/>
     * 3.院部负责人(ROLE_faculty_charge)   stage_current->2  person_audit->2  audit_status->1,2  stage_current_status->2,4<br/>
     * （2222），（2302）<br/>
     * （2214）<br/>
     * 4.教务处经办人(ROLE_office_manager) stage_current->2  person_audit->3  audit_status->1,2  stage_current_status->2,4<br/>
     * （2322），（2402）<br/>
     * （2314）<br/>
     * 5.教务处负责人(ROLE_office_charge) stage_current->2  person_audit->4  audit_status->1,2  stage_current_status->2,4<br/>
     * （2422），（2502）<br/>
     * （2414）<br/>
     * 6.主管校长(ROLE_school_boss)    stage_current->2  person_audit->5  audit_status->1,2  stage_current_status->3,4<br/>
     * （2523），（3000）<br/>
     * （2514）<br/>
     * <p>
     * 状态说明：
     * person_audit：1（院部经办人）2（院部负责人）3（教务处经办人）4（教务处负责人）5（主管校长）0（无）<br/>
     * audit_status：0（待审核） 1（驳回）2（通过）<br/>
     * stage_current：1（竞赛申报阶段）2（备赛申报阶段）3（竞赛总结阶段）<br/>
     * stage_current_status：1（待上报）2（待审核）3（通过）4（驳回）<br/>
     * </p>
     *
     * @param contestApplyActiveDtoEdit
     * @return
     * @throws DtoNullException
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean updateContestApplyActiveRelationPerson(ContestApplyActiveDtoEdit contestApplyActiveDtoEdit) throws DtoNullException {

        if (Objects.isNull(contestApplyActiveDtoEdit)) {
            throw new DtoNullException("参数为空");
        }

        Integer auditStatus = contestApplyActiveDtoEdit.getAuditStatus();
        Long id = contestApplyActiveDtoEdit.getId();
        // ROLE_..
        List<String> roleCodeList = SecurityService.currentUserRoles();

        validIsEnabled(id);
        // 校验
        validAuditEnabled(id);

        // 设置状态
        OaRequest oaRequest = new OaRequest(roleCodeList, id, auditStatus);
        AbstractOaRequestHandler facultyChargeHandler = new FacultyChargeHandler()
                .setHandler(new FacultyManagerHandler()
                        .setHandler(new OfficeChargeHandler()
                                .setHandler(new OfficeManagerHandler()
                                        .setHandler(new SchoolBossHandler()))));

        return facultyChargeHandler.handlerRequestPersonInfo(oaRequest);

    }

    /**
     * 我的审核（竞赛申请）
     * <p>
     * <p>
     * 根据当前登录的角色进行状态改变：<br/>
     * 初始值：1102 <br/>
     * 2.院部经办人(ROLE_faculty_manager)   stage_current->1  person_audit->1  audit_status->1,2  stage_current_status->2,4<br/>
     * 通过：将当前审核人的审核状态添加记录（1122），再将状态设置为下一个审核人（1202）<br/>
     * 驳回：将当前审核人的审核状态添加记录（1114），该版本流程结束，is_active设为0<br/>
     * 3.院部负责人(ROLE_faculty_charge)   stage_current->1  person_audit->2  audit_status->1,2  stage_current_status->2,4<br/>
     * （1222），（1302）<br/>
     * （1214）<br/>
     * 4.教务处经办人(ROLE_office_manager) stage_current->1  person_audit->3  audit_status->1,2  stage_current_status->2,4<br/>
     * （1322），（1402）<br/>
     * （1314）<br/>
     * 5.教务处负责人(ROLE_office_charge) stage_current->1  person_audit->4  audit_status->1,2  stage_current_status->2,4<br/>
     * （1422），（1502）<br/>
     * （1414）<br/>
     * 6.主管校长(ROLE_school_boss)    stage_current->1  person_audit->5  audit_status->1,2  stage_current_status->3,4<br/>
     * （1523），（2000）<br/>
     * （1514）<br/>
     * <p>
     * 状态说明：
     * person_audit：1（院部经办人）2（院部负责人）3（教务处经办人）4（教务处负责人）5（主管校长） 0（无）<br/>
     * audit_status：0（待审核）, 1（驳回）2（通过）<br/>
     * stage_current：1（竞赛申报阶段）2（备赛申报阶段）3（竞赛总结阶段）<br/>
     * stage_current_status：1（待上报）2（待审核）3（通过）4（驳回）<br/>
     * </p>
     *
     * @param contestApplyActiveDtoEdit
     * @return
     * @throws DtoNullException
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean updateContestApplyActiveById(ContestApplyActiveDtoEdit contestApplyActiveDtoEdit) throws DtoNullException {

        if (Objects.isNull(contestApplyActiveDtoEdit)) {
            throw new DtoNullException("参数为空");
        }

        Integer auditStatus = contestApplyActiveDtoEdit.getAuditStatus();
        Long id = contestApplyActiveDtoEdit.getId();
        // ROLE_..
        List<String> roleCodeList = SecurityService.currentUserRoles();

        validIsEnabled(id);
        // 校验
        validAuditEnabled(id);

        // 设置状态
        OaRequest oaRequest = new OaRequest(roleCodeList, id, auditStatus);
        AbstractOaRequestHandler facultyChargeHandler = new FacultyChargeHandler()
                .setHandler(new FacultyManagerHandler()
                        .setHandler(new OfficeChargeHandler()
                                .setHandler(new OfficeManagerHandler()
                                        .setHandler(new SchoolBossHandler()))));

        return facultyChargeHandler.handlerRquest(oaRequest);

    }


    /**
     * 放弃比赛：将流程停掉（主要功能就是修改人员信息）
     *
     * @param contestApplyActiveDto
     * @return
     * @throws DtoNullException
     */
    @Override
    public boolean forbiddenContestApplyActive(ContestApplyActiveDto contestApplyActiveDto) throws DtoNullException {

        if (Objects.isNull(contestApplyActiveDto))
            throw new DtoNullException("参数为空");

        // 停止流程
        ContestApplyRecordModel lastVersion = getLastVersion(contestApplyActiveDto.getId());
        if (Objects.nonNull(lastVersion)) lastVersion.setIsActive(false);

        // 回到流程第二阶段初始状态（支持修改人员信息）
        ContestApplyActiveModel contestApplyActiveModel = ContestApplyActiveConverter.dtoToModel(contestApplyActiveDto);
        setStatus(AuditDict.STAGE_STAGE_SECOND, AuditDict.ROLE_INNER_NO, AuditDict.ROLE_INNER_NO, AuditDict.CURRENT_STATUS_NOT_REPORT, contestApplyActiveModel);

        return super.updateById(contestApplyActiveModel);

    }


    /**
     * 我的审核查询
     *
     * @param contestApplyActiveQuery
     * @return
     * @throws DtoNullException
     */
    @DataPermission
    @Override
    public DataResult<List<ContestApplyActiveVo>> queryContestApplyActive(ContestApplyActiveQuery contestApplyActiveQuery) throws DtoNullException {

        if (Objects.isNull(contestApplyActiveQuery)) {
            throw new DtoNullException("查询条件为空");
        }

        LambdaQueryWrapper<ContestApplyActiveModel> wrapper = new LambdaQueryWrapper<>();
        if (Objects.nonNull(contestApplyActiveQuery.getPageNo()) && Objects.nonNull(contestApplyActiveQuery.getPageSize())) {
            PageHelper.startPage(contestApplyActiveQuery.getPageNo(), contestApplyActiveQuery.getPageSize());
        }
        if (Objects.nonNull(contestApplyActiveQuery.getStageCurrent())) {
            wrapper.eq(ContestApplyActiveModel::getStageCurrent, contestApplyActiveQuery.getStageCurrent());
        }
        wrapper.orderByDesc(ContestApplyActiveModel::getCreateTime);

        List<ContestApplyActiveModel> contestApplyActiveModelList = super.list(wrapper);
        PageInfo<ContestApplyActiveModel> pageInfo = new PageInfo<>(contestApplyActiveModelList);
        PageHelper.clearPage();

        /**
         * 统一结果返回
         */
        DataResult<List<ContestApplyActiveVo>> dataResult = new DataResult<>();
        dataResult.setData(ContestApplyActiveConverter.modelToVoList(pageInfo.getList()));
        PageInfoUtil.setPageInfo(dataResult, pageInfo);

        return dataResult;

    }

    /**
     * 我的竞赛申请
     *
     * @param contestApplyActiveQuery
     * @return
     * @throws DtoNullException
     */
    @Override
    public DataResult<List<ContestApplyActiveVo>> queryContestApplyUserActive(ContestApplyActiveQuery contestApplyActiveQuery) throws DtoNullException {

        if (Objects.isNull(contestApplyActiveQuery)) {
            throw new DtoNullException("查询条件为空");
        }

        LambdaQueryWrapper<ContestApplyActiveModel> wrapper = new LambdaQueryWrapper<>();

        // 条件限制，只返回当前用户创建的（数据权限）
        wrapper.eq(ContestApplyActiveModel::getCreator, SecurityService.currentUserName());

        if (Objects.nonNull(contestApplyActiveQuery.getPageNo()) && Objects.nonNull(contestApplyActiveQuery.getPageSize())) {
            PageHelper.startPage(contestApplyActiveQuery.getPageNo(), contestApplyActiveQuery.getPageSize());
        }
        wrapper.orderByDesc(ContestApplyActiveModel::getCreateTime);

        List<ContestApplyActiveModel> contestApplyActiveModelList = super.list(wrapper);
        PageInfo<ContestApplyActiveModel> pageInfo = new PageInfo<>(contestApplyActiveModelList);
        PageHelper.clearPage();

        // 我的申报状态修正
        List<ContestApplyActiveVo> contestApplyActiveVoList = ContestApplyActiveConverter.modelToVoList(pageInfo.getList());
        List<ContestApplyRecordModel> list = iContestApplyRecordService.list();

        List<ContestApplyActiveVo> data = Objects.nonNull(contestApplyActiveVoList) ? contestApplyActiveVoList.stream().peek(var1 -> {

            Long id = var1.getId();
            // 唯一
            List<ContestApplyRecordModel> collect = Objects.nonNull(list) && !list.isEmpty() ? list.stream().filter(var2 ->
                    var2.getRelation().equals(id)
            ).filter(var3 -> {

                String lastVersion = getLastVersion(Objects.nonNull(list) && !list.isEmpty() ? list.stream().filter(var4 -> var4.getRelation().equals(var3.getRelation())).map(ContestApplyRecordModel::getVersion).collect(Collectors.toList()) : null);

                return Objects.nonNull(lastVersion) ? var3.getVersion().equals(lastVersion) : true;

            }).collect(Collectors.toList()) : null;

//            var1.setStageCurrentStatus((Objects.nonNull(collect) && !collect.isEmpty()) ? collect.get(0).getStageCurrentStatus() : var1.getStageCurrentStatus());

        }).collect(Collectors.toList()) : null;

        /**
         * 统一结果返回
         */
        DataResult<List<ContestApplyActiveVo>> dataResult = new DataResult<>();
        dataResult.setData(data);
        PageInfoUtil.setPageInfo(dataResult, pageInfo);

        return dataResult;

    }

    @DataPermission
    @Override
    public DataResult<Map<String, Object>> queryContestApplyActiveById(Long activeId) throws DtoNullException {

        if (Objects.isNull(activeId)) {
            throw new DtoNullException("ID为空");
        }

        ContestApplyActiveModel contestApplyActiveModel = super.getById(activeId);

        // 关闭数据权限
        PermissionFlag.set(false);

        // 相关资源
        LambdaQueryWrapper<ResourceModel> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ResourceModel::getActiveId, activeId);
        List<ResourceModel> resourceModels = iResourceService.list(wrapper);

        // 相关人员
        LambdaQueryWrapper<ContestPersonInfoModel> personWrapper = new LambdaQueryWrapper<>();
        personWrapper.eq(ContestPersonInfoModel::getActiveId, activeId);
        List<ContestPersonInfoModel> personInfoModelList = iContestPersonInfoService.list(personWrapper);

        // 相关竞赛结果
        LambdaQueryWrapper<ContestResultModel> resultWrapper = new LambdaQueryWrapper<>();
        resultWrapper.eq(ContestResultModel::getActiveId, activeId);
        List<ContestResultModel> resultModelList = iContestResultService.list(resultWrapper);

        /**
         * 统一结果返回
         */
        Map<String, Object> data = new HashMap<>();
        data.put("contest", ContestApplyActiveConverter.modelToVo(contestApplyActiveModel));
        data.put("resource", Objects.nonNull(contestApplyActiveModel) ? ResourceConverter.modelToVoList(resourceModels) : null);
        data.put("persons", Objects.nonNull(contestApplyActiveModel) ? ContestPersonInfoConverter.modelToVoList(personInfoModelList) : null);
        data.put("result", Objects.nonNull(contestApplyActiveModel) ? ContestResultConverter.modelToVoList(resultModelList) : null);
        DataResult<Map<String, Object>> dataResult = new DataResult<>();
        dataResult.setData(data);

        return dataResult;

    }

    /**
     * @param stageCurrent       流程阶段编号
     * @param personAudit        审核人
     * @param auditStatus        审核状态
     * @param stageCurrentStatus 流程阶段的审核状态
     */
    protected void setStatus(Integer stageCurrent,
                             Integer personAudit,
                             Integer auditStatus,
                             Integer stageCurrentStatus,
                             ContestApplyActiveModel model) {

        if (Objects.isNull(model)) return;

        model.setStageCurrent(stageCurrent);
        model.setPersonAudit(personAudit);
        model.setAuditStatus(auditStatus);
        model.setStageCurrentStatus(stageCurrentStatus);

    }

    /**
     * 根据当前活动表ID获取相关的活动记录的最新版本的数据
     *
     * @param id
     * @return
     */
    protected ContestApplyRecordModel getLastVersion(Long id) {

        if (Objects.isNull(id)) return null;

        Map<String, Object> wrap = new HashMap<>();
        wrap.put("relation", id);
        List<ContestApplyRecordModel> contestApplyRecordModelList = iContestApplyRecordService.listByMap(wrap);
        return Objects.nonNull(contestApplyRecordModelList) && !contestApplyRecordModelList.isEmpty() ? contestApplyRecordModelList.stream().filter(obj ->
                obj.getVersion().equals(getLastVersion(contestApplyRecordModelList.stream().map(var1 -> var1.getVersion()).collect(Collectors.toList())))
        ).collect(Collectors.toList()).get(0) : null;

    }

    /**
     * 返回申请记录中的最新版本
     *
     * @param versions
     * @return
     */
    protected String getLastVersion(List<String> versions) {

        if (Objects.isNull(versions)) {
            return null;
        }

        return String.valueOf(versions.stream().mapToDouble(Double::valueOf).max().getAsDouble());

    }

    /**
     * 防止在审核
     *
     * @param id
     * @throws DtoNullException
     */
    protected void validAuditEnabled(Long id) throws DtoNullException {

        ContestApplyRecordModel lastVersion = getLastVersion(id);

        if (!lastVersion.getIsActive()) {
            throw new DtoNullException("流程结束，不可审核");
        }

    }

    /**
     * 验证数据是否存在
     *
     * @param id
     * @throws DtoNullException
     */
    protected void validIsEnabled(Long id) throws DtoNullException {

        if (Objects.isNull(super.getById(id)))
            throw new DtoNullException("流程不存在，不可审核");

    }

    /**
     * 获取文件信息
     *
     * @param file
     * @return
     */
    protected FileBase getFileInfo(MultipartFile file) {

        if (file == null) {
            return null;
        }

        FileBase fileBase = new FileBase();

        String filename = file.getOriginalFilename();
        String objectName = StrUtil.format("{}/{}/{}/{}", "赛事申请", DateUtil.format(new Date(), "yyyy/MM/dd"), System.currentTimeMillis(), filename);
        long size = file.getSize();

        fileBase.setUrl(objectName);
        fileBase.setName(filename);
        fileBase.setSize(size / (1024.0 * 1024.0));
        fileBase.setExtensionName(MinioUtil.getExtensionName(filename));
        fileBase.setIsSuccess(Boolean.TRUE);
        fileBase.setType(ResourceType.RESOURCE_TYPE_FIRST);
        fileBase.setFileSize(file.getSize());

        return fileBase;

    }

    /**
     * 异步上传
     *
     * @param contestApplyActiveModel
     * @param contestApplyRecordModel
     * @param contestApplyActiveDto
     * @throws IOException
     */
    protected void fileUploadAsync(ContestApplyActiveModel contestApplyActiveModel,
                                   ContestApplyRecordModel contestApplyRecordModel,
                                   ContestApplyActiveDto contestApplyActiveDto) throws IOException {

        if (contestApplyActiveDto.getFile() == null) {
            return;
        }

        MultipartFile file = contestApplyActiveDto.getFile();
        InputStream fileInputStream = file.getInputStream();
        FileBase fileInfo = getFileInfo(file);
        AsyncManager.me().execute(AsyncFactory.fileUploadAsync(
                fileInfo,
                file.getContentType(),
                contestApplyActiveDto.getOptionType(),
                fileInputStream,
                contestApplyActiveModel,
                contestApplyRecordModel));

    }

    /**
     * BeanCopy忽略字段
     *
     * @return
     */
    protected String[] ignoreAttribute(List<String> ignores) {

        List<String> defaultIgnoreStr = new ArrayList<>(4);

        defaultIgnoreStr.addAll(Arrays.asList("updater", "createTime", "updateTime", "deleted"));

        if (Objects.nonNull(ignores) && !ignores.isEmpty())
            defaultIgnoreStr.addAll(ignores);

        return ArrayUtil.toArray(defaultIgnoreStr, String.class);

    }

    /**
     * 验证是否填写了人员信息
     *
     * @param activeId
     */
    protected void validPersonInfoIsEnabled(Long activeId) throws DtoNullException {

        LambdaQueryWrapper<ContestPersonInfoModel> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ContestPersonInfoModel::getActiveId, activeId);
        List<ContestPersonInfoModel> temp = iContestPersonInfoService.list(wrapper);
        if (Objects.isNull(temp) || temp.isEmpty()) throw new DtoNullException("请填报竞赛人员信息");

    }


    /**
     * 验证是否可以进行
     */
    protected void validContestResultEnabled(Long applyId) throws DtoNullException {

        Long activeId = iContestApplyService.getById(applyId).getRelation();

        if (Objects.nonNull(activeId)) {

            ContestApplyActiveModel temp = super.getById(activeId);

            if (Objects.nonNull(temp)) {
                if (!(AuditDict.STAGE_STAGE_THIRD.equals(temp.getStageCurrent()) &&
                        AuditDict.ROLE_INNER_NO.equals(temp.getPersonAudit()) &&
                        AuditDict.AUDIT_STATUS_NO.equals(temp.getAuditStatus()) &&
                        AuditDict.CURRENT_STATUS_NOT_REPORT.equals(temp.getStageCurrentStatus()))) {

                    throw new DtoNullException("当前流程状态不可操作");

                }
            }

        }

    }

    /**
     * 验证是否可以进行
     *
     * @param activeId
     * @throws DtoNullException
     */
    protected void validContestPersonInfoEnabled(Long activeId) throws DtoNullException {

        if (Objects.nonNull(activeId)) {

            ContestApplyActiveModel temp = super.getById(activeId);

            if (Objects.nonNull(temp)) {
                if (!(AuditDict.STAGE_STAGE_SECOND.equals(temp.getStageCurrent()) &&
                        AuditDict.ROLE_INNER_NO.equals(temp.getPersonAudit()) &&
                        AuditDict.AUDIT_STATUS_NO.equals(temp.getAuditStatus()) &&
                        AuditDict.CURRENT_STATUS_NOT_REPORT.equals(temp.getStageCurrentStatus()))) {

                    throw new DtoNullException("当前流程状态不可操作");

                }
            }

        }

    }

    /**
     * 验证赛事是否可以进行
     *
     * @param contestApplyActiveDto
     */
    protected void validContestEnabled(ContestApplyActiveDto contestApplyActiveDto) throws DtoNullException {

        List<ContestApplyActiveModel> contestApplyActiveModels = super.list();

        if (Objects.nonNull(contestApplyActiveModels) && !contestApplyActiveModels.isEmpty()) {

            for (ContestApplyActiveModel contestApplyActiveModel : contestApplyActiveModels) {
                if (contestApplyActiveDto.getEventName().trim().equals(contestApplyActiveModel.getEventName()) &&
                        contestApplyActiveDto.getCategory().trim().equals(contestApplyActiveModel.getCategory()) &&
                        contestApplyActiveDto.getCompetitionName().trim().equals(contestApplyActiveModel.getCompetitionName()) &&
                        contestApplyActiveDto.getRaceType().trim().equals(contestApplyActiveModel.getRaceType()) &&
                        contestApplyActiveDto.getTrack().equals(contestApplyActiveModel.getTrack()))

                    throw new DtoNullException("当前赛事已存在，不可申请");

            }
        }


    }

    /**
     * 验证是否可以修改
     *
     * @param contestApplyActiveDto
     */
    protected void validUpdateEnabled(ContestApplyActiveDto contestApplyActiveDto) throws DtoNullException {

        if (Objects.nonNull(contestApplyActiveDto)) {

            ContestApplyActiveModel temp = super.getById(contestApplyActiveDto.getId());

            if (Objects.nonNull(temp)) {
                if (!(AuditDict.STAGE_STAGE_NO.equals(temp.getStageCurrent()) &&
                        AuditDict.ROLE_INNER_NO.equals(temp.getPersonAudit()) &&
                        AuditDict.AUDIT_STATUS_NO.equals(temp.getAuditStatus()) &&
                        AuditDict.CURRENT_STATUS_NOT_REPORT.equals(temp.getStageCurrentStatus()))) {

                    throw new DtoNullException("当前流程状态不可操作");

                }
            }

        }

    }

    /**
     * 一次流程记录的最大版本书为：99.0-99.9
     *
     * @param lastVersion
     * @return
     * @throws DtoNullException
     */
    protected Double getVersionPrefix(String lastVersion) throws DtoNullException {

        double version = Double.parseDouble(lastVersion);
        if (version < 10.0) {
            return Double.valueOf(lastVersion.substring(0, 1));
        } else if (version < 100.0) {
            return Double.valueOf(lastVersion.substring(0, 2));
        } else {
            throw new DtoNullException("版本号超出最大范围");
        }

    }

    /**
     * 验证当前申报的赛事是否存在(默认是验证启用的赛事及其赛项)
     *
     * @param contestApplyActiveDto
     * @return
     */
    protected boolean validContestIsHas(ContestApplyActiveDto contestApplyActiveDto) throws DtoNullException {

        List<CompetitionVo> competitionVos = iCompetitionService.competitionListSelect().getResult();
        if (Objects.nonNull(competitionVos) && !competitionVos.isEmpty()) {
            for (CompetitionVo competitionVo : competitionVos) {
                if (competitionVo.getCompetitionName().equals(contestApplyActiveDto.getEventName())) {
                    for (CompetitionItemVo competitionItemVo : competitionVo.getCompetitionItemVos()) {
                        if (competitionItemVo.getItemName().equals(contestApplyActiveDto.getCompetitionName())) {
                            return true;
                        }
                    }
                }
            }
        }

        return false;

    }

}
